home *** CD-ROM | disk | FTP | other *** search
/ Programmer Plus 2007 / Programmer-Plus-2007.iso / Programming / Borland Plateform / TURBO PASCAL 1.5 for WIN / PAINT.PAK / TOOLS.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1992-06-08  |  17.0 KB  |  607 lines

  1. {************************************************}
  2. {                                                }
  3. {   Turbo Pascal for Windows: Paint Demo         }
  4. {   Tools unit                                   }
  5. {   Copyright (c) 1992 by Borland International  }
  6. {                                                }
  7. {************************************************}
  8.  
  9. unit Tools;
  10.  
  11. { This unit supplies the actual painting and drawing tools for the paint
  12.   program. Each tool manipulates the bits in a display context in a
  13.   specialized manner. The behaviour of each tool is defined. The icon and
  14.   cursor associated with a tool is specified when the tool is created, but
  15.   the tool itself does not make use of this information.
  16. }
  17.  
  18. interface
  19.  
  20. uses PaintDef, Rect,
  21.      Strings, WinTypes, WinProcs, WObjects;
  22.  
  23. type
  24.   { A Draw Tool is a tool whose action is instigated solely by mouse input.
  25.     The action is always fully performed within a single Mouse Down, Mouse
  26.     Move, Mouse Up cycle.
  27.  
  28.     TDrawTool performs the actions necessary to maintain the drawing
  29.     environment (storing window, display context, etc.) so that each tool need
  30.     only implement those of DrawBegin (called on Mouse Down), DrawTo (called
  31.     on Mouse Move) and DrawEnd (called on MouseUp) that perform actions 
  32.     peculiar to that tool.
  33.   }
  34.   PDrawTool = ^TDrawTool;            { Defined in PaintDef }
  35.   TDrawTool = object(TPaintTool)
  36.     Pen, MemPen: HPen;                    { The pens not in use }
  37.     Brush, MemBrush: HBrush;            { The brushes not in use }
  38.  
  39.     { Mouse responses }
  40.     procedure MouseDown(AWindow: HWnd; X, Y: Integer;
  41.       AState: PState); virtual;
  42.     procedure MouseMove(X, Y: Integer); virtual;
  43.     procedure MouseUp; virtual;
  44.   end;
  45.  
  46.   { A Pen tool draws a freeform line using the currently selected pen color
  47.     and width.
  48.   }
  49.   PPenTool = ^TPenTool;
  50.   TPenTool = object(TDrawTool)
  51.  
  52.     { Actual drawing }
  53.     procedure DrawBegin(X, Y: Integer); virtual;
  54.     procedure DrawTo(X, Y: Integer); virtual;
  55.   end;
  56.  
  57.   { An Eraser tool draws a freeform white line using the currently selected
  58.     pen width.
  59.   }
  60.   PEraserTool = ^TEraserTool;
  61.   TEraserTool = object(TPenTool)
  62.     Eraser, MemEraser: HPen;            { The pens not in use }
  63.  
  64.     { Actual drawing }
  65.     procedure DrawBegin(X, Y: Integer); virtual;
  66.     procedure DrawEnd; virtual;
  67.   end;
  68.  
  69.   { A Fill tool fills an area bounded by the current pen color with the
  70.     current brush color.
  71.   }
  72.   PFillTool = ^TFillTool;
  73.   TFillTool = object(TDrawTool)
  74.  
  75.     { Actual drawing }
  76.     procedure DrawBegin(X, Y: Integer); virtual;
  77.   end;
  78.  
  79.   { A Box tool is a tool that operates on a rectangularly bounded area. These
  80.     are tools whose actual drawing calls involve specifying this bounding
  81.     rectangle, e.g., for drawing a rectangle or oval, OR that perform
  82.     rubberbanding during drawing.
  83.   }
  84.   PBoxTool = ^TBoxTool;
  85.   TBoxTool = object(TDrawTool)
  86.     Filled: Boolean;             { Should the internal area be colored }
  87.     X1, Y1, X2, Y2: Integer;         { The bounding rectangle }
  88.  
  89.     { Creation }
  90.     constructor Init(AState: PState; IconName, CursorName: PChar; 
  91.                      AFilled: Boolean);
  92.  
  93.     { Actual drawing }
  94.     procedure DrawBegin(X, Y: Integer); virtual;
  95.     procedure DrawTo(X, Y: Integer); virtual;
  96.     procedure DrawEnd; virtual;
  97.     procedure DrawObject(aDC: HDC); virtual;
  98.   end;
  99.  
  100.   { A Rect tool is a tool that draws (or manipulates) a rectangle.
  101.   }
  102.   PRectTool = ^TRectTool;
  103.   TRectTool = object(TBoxTool)
  104.  
  105.     { Actual drawing }
  106.     procedure DrawObject(aDC: HDC); virtual;
  107.   end;
  108.  
  109.   { A Select tool selects and maintains a rectangular subset (the current 
  110.     selection) of the image. The selection may serve only to specify this
  111.     subset, or it may actively be manipulated (e.g., by dragging).
  112.     If it is used for dragging a separate bitmap is created that exactly
  113.     contains the portion of the image selected.
  114.   }
  115.   PSelectTool = ^TSelectTool;
  116.   TSelectTool = object(TRectTool)
  117.     SelectionDC: HDC;        { Display context for the current selection }
  118.  
  119.     { Creation }
  120.     constructor Init(AState: PState; IconName, CursorName: PChar;
  121.       AFilled: Boolean);
  122.  
  123.     { Re-initilization }
  124.     procedure Deselect; virtual;
  125.  
  126.     { Actual drawing }
  127.     procedure DrawBegin(X, Y: Integer); virtual;
  128.     procedure DrawTo(X, Y: Integer); virtual;
  129.     procedure DrawEnd; virtual;
  130.     procedure DrawObject(aDC: HDC); virtual;
  131.  
  132.     { Utilities }
  133.     procedure PickUpSelection(aDC: HDC; Left, Top, Width, Height: Integer);
  134.       virtual;
  135.     procedure ReleaseSelection; virtual;
  136.     procedure DropSelection; virtual;
  137.   end;
  138.  
  139.   { An Ellipse tool is a tool that draws an ellipse.
  140.   }
  141.   PEllipseTool = ^TEllipseTool;
  142.   TEllipseTool = object(TBoxTool)
  143.  
  144.     { Actual drawing }
  145.     procedure DrawObject(aDC: HDC); virtual;
  146.   end;
  147.  
  148.   { A Line tool draws a straight line.
  149.   }
  150.   PLineTool = ^TLineTool;
  151.   TLineTool = object(TBoxTool)
  152.     
  153.     { Actual drawing }
  154.     procedure DrawObject(aDC: HDC); virtual;
  155.   end;
  156.  
  157.  
  158. implementation
  159.  
  160. { TDrawTool }
  161.  
  162. { Set up the drawing environment for any drawing tool. Note that the
  163.   display context for the off-screen bitmap has already been set up.
  164.   
  165.   Since shared display contexts are used for the window, they should
  166.   be held as shortly as possible. Hence the display context for the window
  167.   is retrieve on each operation.
  168.  
  169. }
  170. procedure TDrawTool.MouseDown(AWindow: HWnd; X, Y: Integer; AState: PState);
  171. begin
  172.   { Set up the window and state }
  173.   Window := AWindow;
  174.   State := AState;
  175.  
  176.   { Direct all mouse input to Window }
  177.   SetCapture(Window);
  178.  
  179.   { Create the actual pens and brushes to be used }
  180.   Pen := CreatePen(ps_Solid, State^.PenSize, State^.PenColor); 
  181.   MemPen := CreatePen(ps_Solid, State^.PenSize, State^.PenColor); 
  182.   Brush := CreateSolidBrush(State^.BrushColor);
  183.   MemBrush := CreateSolidBrush(State^.BrushColor);
  184.  
  185.   { Set up the display contexts }
  186.   DC := GetDC(Window);        
  187.   SelectObject(DC, Pen);
  188.   SelectObject(State^.MemDC, MemPen);
  189.   SelectObject(DC, Brush);
  190.   SelectObject(State^.MemDC, MemBrush);
  191.  
  192.   DrawBegin(X, Y);          { Tell the tool to start drawing }
  193. end;
  194.  
  195. procedure TDrawTool.MouseMove(X, Y: Integer);
  196. begin
  197.   DrawTo(X, Y);              { Tell the tool to do its draw thing }
  198. end;
  199.  
  200. procedure TDrawTool.MouseUp;
  201. begin
  202.   DrawEnd;              { Tell the tool to stop drawing }
  203.  
  204.   { Clean up }
  205.   { Reset mouse input }
  206.   ReleaseCapture;
  207.  
  208.   { Restore display contexts }
  209.   SelectObject(DC, GetStockObject(Black_Pen));
  210.   SelectObject(State^.MemDC, GetStockObject(Black_Pen));
  211.   SelectObject(DC, GetStockObject(White_Brush));
  212.   SelectObject(State^.MemDC, GetStockObject(White_Brush));
  213.   
  214.   { Delete the created objects }
  215.   DeleteObject(Pen);
  216.   DeleteObject(MemPen);
  217.   DeleteObject(Brush);
  218.   DeleteObject(MemBrush);
  219.  
  220.   ReleaseDC(Window, DC);
  221. end;
  222.  
  223. { TPenTool }
  224.  
  225. { Actual drawing }
  226. procedure TPenTool.DrawBegin(X, Y: Integer);
  227. begin
  228.   MoveTo(DC, X, Y);                { Move the pen position }
  229.   MoveTo(State^.MemDC, X+State^.Offset.X, Y+State^.Offset.Y);    { Echo }
  230.   DrawTo(X, Y);                    { Draw the initial pixel(s) }
  231. end;
  232.  
  233. procedure TPenTool.DrawTo(X, Y: Integer);
  234. begin
  235.   LineTo(DC, X, Y);                { Draw a line from the pen position }
  236.   LineTo(State^.MemDC, X+State^.Offset.X, Y+State^.Offset.Y);    { Echo }
  237. end;
  238.  
  239. { TEraserTool }
  240.  
  241. { Actual drawing }
  242. procedure TEraserTool.DrawBegin(X, Y: Integer);
  243. begin
  244.   { Create an erasing pen and reset the display context }
  245.   Eraser := CreatePen(ps_Solid, State^.PenSize, $FFFFFF);
  246.   MemEraser := CreatePen(ps_Solid, State^.PenSize, $FFFFFF);
  247.   SelectObject(DC, Eraser);
  248.   SelectObject(State^.MemDC, MemEraser);
  249.  
  250.   SelectObject(DC, GetStockObject(White_Brush));
  251.   SelectObject(State^.MemDC, GetStockObject(White_Brush));
  252.  
  253.   TPenTool.DrawBegin(X, Y);    { Start drawing }
  254. end;
  255.  
  256. procedure TEraserTool.DrawEnd;
  257. begin
  258.   { Clean up }
  259.   SelectObject(DC, Pen);
  260.   SelectObject(State^.MemDC, MemPen);
  261.   DeleteObject(Eraser);
  262.   DeleteObject(MemEraser);
  263. end;
  264.  
  265. { TFillTool }
  266.  
  267. procedure TFillTool.DrawBegin(X, Y: Integer);
  268. begin
  269.  FloodFill(DC, X, Y, State^.PenColor);    { Fills the area bounded by
  270.                                           PenColor }
  271.  FloodFill(State^.MemDC, X, Y, State^.PenColor); { Echo }
  272. end;
  273.  
  274. { TBoxTool }
  275.  
  276. { Creation }
  277. constructor TBoxTool.Init(AState: PState; IconName, CursorName:
  278.   PChar; AFilled: Boolean);
  279. begin
  280.   TDrawTool.Init(AState, IconName, CursorName);
  281.   Filled := AFilled;        { Record whether tool operates on outline }
  282.                                 { or outline and bounded area }
  283. end;
  284.  
  285. { Actual drawing }
  286. { During the drawing a BoxTool rubberbands a black outline of the final 
  287.   object on the screen by alternately erasing and redrawing the outline. }
  288. procedure TBoxTool.DrawBegin(X, Y: Integer);
  289. begin
  290.   X1 := X;            { Initially the rectangle is a single pixel }
  291.   Y1 := Y;
  292.   X2 := X;
  293.   Y2 := Y;
  294.  
  295.   { Set up the display context to draw a black outline during drawing }
  296.   SelectObject(DC, GetStockObject(Black_Pen));
  297.   SelectObject(DC, GetStockObject(Null_Brush));
  298.  
  299.   { Invert pixels under the pen }
  300.   SetROP2(DC, r2_Not);
  301.  
  302.   { Draw the initial outline }
  303.   DrawObject(DC);
  304. end;
  305.  
  306. procedure TBoxTool.DrawTo(X, Y: Integer);
  307. begin
  308.   { Draw over the outline last drawn. Since the pen inverts pixels and is
  309.     black this will erase the last outline. }
  310.   DrawObject(DC);
  311.  
  312.   { Update the rectangle to be operated on }
  313.   X2 := X;    
  314.   Y2 := Y;
  315.  
  316.   { Draw the new outline }
  317.   DrawObject(DC);
  318. end;
  319.  
  320. procedure TBoxTool.DrawEnd;
  321. begin
  322.   { Erase the last outline drawn }
  323.   DrawObject(DC);
  324.  
  325.   { Set up the display context to draw the real image }  
  326.   SetROP2(DC, r2_CopyPen);
  327.   SelectObject(DC, Pen);
  328.   if Filled then 
  329.     SelectObject(DC, Brush)
  330.   else
  331.     SelectObject(State^.MemDC, GetStockObject(Null_Brush));
  332.  
  333.   { Draw the actual image }
  334.   DrawObject(DC);
  335.   with State^ do
  336.   begin
  337.     X1 := X1 + Offset.X;
  338.     Y1 := Y1 + Offset.Y;
  339.     X2 := X2 + Offset.X;
  340.     Y2 := Y2 + Offset.Y;
  341.   end;
  342.   DrawObject(State^.MemDC);
  343. end;
  344.  
  345. { Allow the real tool to specify the image it draws.
  346. }
  347. procedure TBoxTool.DrawObject(aDC: HDC);
  348. begin
  349. end;
  350.  
  351. { TRectTool }
  352.  
  353. { Draw a rectangle.
  354. }
  355. procedure TRectTool.DrawObject(aDC: HDC);
  356. begin
  357.   Rectangle(aDC, X1, Y1, X2, Y2);
  358. end;
  359.  
  360. { TSelectTool }
  361.  
  362. { Creation }
  363. constructor TSelectTool.Init(AState: PState; IconName, CursorName: PChar;
  364.   AFilled: Boolean);
  365. begin
  366.   TRectTool.Init(AState, IconName, CursorName, AFilled);
  367.   SelectionDC := 0;
  368. end;
  369.  
  370. { Utility }
  371. { Make sure there is no active selection before exiting. If there is an image
  372.   in the selection paste it into the current image.
  373. }
  374. procedure TSelectTool.Deselect;
  375. begin
  376.   DropSelection;
  377. end;
  378.  
  379. { Actual drawing }
  380. { The selection tool has two states of operation: While the selection is
  381.   being made, it operates as a rectangle tool. If a selection has been made
  382.   and the mouse clicks on it, the selection is dragged with the mouse.
  383.  
  384.   SelectionDC is valid only during dragging and thus serves as the
  385.   flag to distinguish the two modes during drawing.
  386.  
  387.   Dragging the selection is effected by creating a copy (i.e., a
  388.   bitmap) of the selection and alternately restoring the screen to the
  389.   original (actually, only restoring those pieces that are revealed by
  390.   moving the selection), and copying the selection bitmap to the screen.
  391.  
  392.   Throughout dragging
  393.     X1, Y1 contains the previous mouse position
  394.     State^.Selection contains the current coordinates of the selection
  395. }
  396. procedure TSelectTool.DrawBegin(X, Y: Integer);
  397. var
  398.   Pt: TPoint;
  399. begin
  400.   { Check to see if there is a hit on the selection }
  401.   Pt.X := X;
  402.   Pt.Y := Y;
  403.   if PtInRect(State^.Selection, Pt) then
  404.     { Drag selection }
  405.   begin
  406.     { Last mouse position }
  407.     X1 := X;
  408.     Y1 := Y;
  409.  
  410.     { Create the selection bitmap if necessary. (It may already have been
  411.       created, for example through a Paste operation.) }
  412.     if State^.SelectionBM = 0 then
  413.       with State^.Selection, State^ do
  414.       begin
  415.     PickUpSelection(MemDC, Left + Offset.X, Top + Offset.Y,
  416.       Right-Left, Bottom-Top);
  417.  
  418.         { The convention is to cut the selection, so white out
  419.           the hole }
  420.     PatBlt(MemDC, Left + Offset.X, Top + Offset.Y,
  421.       Right - Left, Bottom - Top, Whiteness);
  422.       end;
  423.  
  424.     { Set up the selection display context }
  425.     SelectionDC := CreateCompatibleDC(DC);
  426.     State^.SelectionBM := SelectObject(SelectionDC, State^.SelectionBM);
  427.   end
  428.   else
  429.   { Make new selection }
  430.   begin
  431.     { Paste down the old one if there is one }
  432.     DropSelection;
  433.     TRectTool.DrawBegin(X, Y);
  434.   end;
  435. end;
  436.  
  437. procedure TSelectTool.DrawTo(X, Y: Integer);
  438. var
  439.   I, Count: Integer;        { Number of rectangles that must be restored }
  440.   MoveX, MoveY: Integer;    { Change in X, Y coordinates of selection }
  441.   Result: RectArray;        { Rectangles that must be restored }
  442.   NewCoords: TRect;        { The new coordinates of selection }
  443. begin
  444.   if SelectionDC <> 0 then    { Dragging }
  445.   begin
  446.  
  447.     { Figure out the new coordinates }
  448.     MoveX := X - X1;
  449.     MoveY := Y - Y1;
  450.     with State^.Selection do
  451.       SetRect(NewCoords, Left + MoveX, Top + MoveY, Right + MoveX,
  452.         Bottom + MoveY);
  453.  
  454.     { Determine the area that must be repainted. Note that this will always
  455.       be 0, 1, or 2 rectangles exactly }
  456.     Count := SubtractRect(Result, State^.Selection, NewCoords);
  457.  
  458.     { Repaint the rectangles revealed by the move }
  459.     for I := 0 to Count-1 do
  460.       with Result[I], State^ do
  461.     BitBlt(DC, Left, Top, Right - Left, Bottom - Top,
  462.       State^.MemDC, Left + Offset.X, Top + Offset.Y, SrcCopy);
  463.  
  464.     { Update and repaint the selection }
  465.     with NewCoords do
  466.       SetRect(State^.Selection, Left, Top, Right, Bottom);
  467.     X1 := X;
  468.     Y1 := Y;
  469.     DrawObject(DC);
  470.   end
  471.   else                { Selecting }
  472.     TRectTool.DrawTo(X, Y);
  473. end;
  474.  
  475. procedure TSelectTool.DrawEnd;
  476.  
  477.   procedure Sort(var N1, N2: Integer);
  478.   var
  479.     Temp: Integer;
  480.   begin
  481.     if N1 > N2 then
  482.     begin
  483.       Temp := N1;
  484.       N1 := N2;
  485.       N2 := Temp;
  486.     end;
  487.   end;
  488.  
  489. begin
  490.   DrawObject(DC);
  491.   if SelectionDC <> 0 then
  492.   begin
  493.     { Clean up }
  494.     State^.SelectionBM := SelectObject(SelectionDC, State^.SelectionBM);
  495.     DeleteDC(SelectionDC);
  496.     SelectionDC := 0;
  497.   end
  498.   else
  499.   begin
  500.     { Update the selection }
  501.     Sort(X1, X2);
  502.     Sort(Y1, Y2);
  503.     SetRect(State^.Selection, X1, Y1, X2, Y2);
  504.   end;
  505. end;
  506.  
  507. procedure TSelectTool.DrawObject(aDC: HDC);
  508. begin
  509.   if SelectionDC <> 0 then
  510.     { Draw the selection bitmap }
  511.     with State^.Selection, State^ do
  512.       BitBlt(aDC, Left, Top, Right-Left, Bottom-Top,
  513.     SelectionDC, 0, 0, SrcCopy)
  514.   else
  515.     { Pretend to be a rectangle }
  516.     TRectTool.DrawObject(aDC)
  517. end;
  518.  
  519. { Utilities }
  520. { Set the selection bitmap to be a bitmap that contains a copy of the
  521.   bits contained in the indicated rectangle of the bitmap in a drawing
  522.   context.
  523. }
  524. procedure TSelectTool.PickUpSelection(aDC: HDC; Left, Top, Width,
  525.   Height: Integer);
  526. var
  527.   SelDC: HDC;            { For copying into the selection bitmap }
  528. begin
  529.   { Paste down the current selection if there is one }
  530.   if State^.SelectionBM <> 0 then DropSelection;
  531.  
  532.   { Set the default screen coordinates for the selection if necessary }
  533.   if IsRectEmpty(State^.Selection) then 
  534.     SetRect(State^.Selection, 0, 0, Width, Height);
  535.   
  536.   { Create the selection bitmap and copy the bits }
  537.   SelDC := CreateCompatibleDC(aDC);
  538.   State^.SelectionBM := CreateCompatibleBitmap(aDC, Width, Height);
  539.   State^.SelectionBM := SelectObject(SelDC, State^.SelectionBM);
  540.   BitBlt(SelDC, 0, 0, Width, Height, aDC, Left, Top, SrcCopy);
  541.  
  542.   { Clean up }
  543.   State^.SelectionBM := SelectObject(SelDC, State^.SelectionBM);
  544.   DeleteDC(SelDC);
  545. end;
  546.  
  547. { Set the current selection to none without copying back the selection bitmap.
  548. }
  549. procedure TSelectTool.ReleaseSelection;
  550. begin
  551.   if not IsRectEmpty(State^.Selection) then
  552.   begin
  553.     InvalidateRect(Window, @State^.Selection, False);
  554.     SetRectEmpty(State^.Selection);
  555.     if State^.SelectionBM <> 0 then
  556.     begin
  557.       DeleteObject(State^.SelectionBM);
  558.       State^.SelectionBM := 0;
  559.     end;
  560.   end;
  561. end;
  562.  
  563. { Set the current selection to none, but paste the selection bitmap down.
  564. }
  565. procedure TSelectTool.DropSelection;
  566. var
  567.   SelDC: HDC;
  568. begin
  569.   if State^.SelectionBM <> 0 then
  570.   begin
  571.     { Mark the bitmap as having been modified }
  572.     State^.IsDirtyBitmap := True;
  573.  
  574.     { Copy the selection bitmap back }
  575.     SelDC := CreateCompatibleDCW(Window);
  576.     State^.SelectionBM := SelectObject(SelDC, State^.SelectionBM);
  577.     with State^.Selection, State^ do
  578.       BitBlt(MemDC, Left + Offset.X, Top + Offset.Y,
  579.         Right + Offset.X, Bottom + Offset.Y, SelDC, 0, 0, SrcCopy);
  580.     State^.SelectionBM := SelectObject(SelDC, State^.SelectionBM);
  581.  
  582.     { Clean up }
  583.     DeleteDC(SelDC);
  584.   end;
  585.   ReleaseSelection;
  586. end;
  587.  
  588. { TEllipseTool }
  589.  
  590. { Draw an ellipse.
  591. }
  592. procedure TEllipseTool.DrawObject(aDC: HDC);
  593. begin
  594.   Ellipse(aDC, X1, Y1, X2, Y2);
  595. end;
  596.  
  597. { TLineTool }
  598.  
  599. { Actual drawing }
  600. procedure TLineTool.DrawObject(aDC: HDC);
  601. begin
  602.   MoveTo(aDC, X1, Y1);
  603.   LineTo(aDC, X2, Y2);
  604. end;
  605.  
  606. end.
  607.